﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using CryptSharp.Utility;
using NUnit.Framework;

namespace StartReading.Tests.Security.Tests.Cryptography
{
    /// <summary>
    /// Test Vectors can be accessed at 
    ///     "Stronger Key Derivation via Sequential Memory-Hard Functions" (Page 16)
    ///     http://www.tarsnap.com/scrypt/scrypt.pdf 
    /// </summary>

    [TestFixture]
    class ScryptTests
    {
        /// <summary>
        /// scrypt(“”, “”, 16, 1, 1, 64) = 77 d6 57 62 38 65 7b 20 3b 19 ca 42 c1 8a 04 97
        ///                                f1 6b 48 44 e3 07 4a e8 df df fa 3f ed e2 14 42
        ///                                fc d0 06 9d ed 09 48 f8 32 6a 75 3a 0f c8 1f 17
        ///                                e8 d3 e0 fb 2e 0d 36 28 cf 35 e2 0c 38 d1 89 06
        /// </summary>
        [Test]
        public void Scrypt_ComputeKey_TestVector1()
        {
            var password = String.Empty;
            var passwordBytes = Encoding.ASCII.GetBytes( password );
            var salt = String.Empty;
            var saltBytes = Encoding.ASCII.GetBytes( salt );
            var nCpuCost = 16;
            var rMemoryCost = 1;
            var pParallelization = 1;
            const int derivedKeyLengthBytes = 64;
            var output = new byte[derivedKeyLengthBytes];

            SCrypt.ComputeKey( passwordBytes, saltBytes, nCpuCost, rMemoryCost, pParallelization, null, output );

            Assert.That( output, Is.EqualTo( new byte[] 
            {
                0x77, 0xd6, 0x57, 0x62, 0x38, 0x65, 0x7b, 0x20, 0x3b, 0x19, 0xca, 0x42, 0xc1, 0x8a, 0x04, 0x97,
                0xf1, 0x6b, 0x48, 0x44, 0xe3, 0x07, 0x4a, 0xe8, 0xdf, 0xdf, 0xfa, 0x3f, 0xed, 0xe2, 0x14, 0x42,
                0xfc, 0xd0, 0x06, 0x9d, 0xed, 0x09, 0x48, 0xf8, 0x32, 0x6a, 0x75, 0x3a, 0x0f, 0xc8, 0x1f, 0x17,
                0xe8, 0xd3, 0xe0, 0xfb, 0x2e, 0x0d, 0x36, 0x28, 0xcf, 0x35, 0xe2, 0x0c, 0x38, 0xd1, 0x89, 0x06,
            } ) );
        }

        /// <summary>
        /// scrypt(“password”, “NaCl”, 1024, 8, 16, 64) = fd ba be 1c 9d 34 72 00 78 56 e7 19 0d 01 e9 fe
        ///                                               7c 6a d7 cb c8 23 78 30 e7 73 76 63 4b 37 31 62
        ///                                               2e af 30 d9 2e 22 a3 88 6f f1 09 27 9d 98 30 da
        ///                                               c7 27 af b9 4a 83 ee 6d 83 60 cb df a2 cc 06 40
        /// </summary>
        [Test]
        public void Scrypt_ComputeKey_TestVector2()
        {
            var password = "password";
            var passwordBytes = Encoding.ASCII.GetBytes( password );
            var salt = "NaCl";
            var saltBytes = Encoding.ASCII.GetBytes( salt );
            var nCpuCost = 1024;
            var rMemoryCost = 8;
            var pParallelization = 16;
            const int derivedKeyLengthBytes = 64;
            var output = new byte[ derivedKeyLengthBytes ];

            SCrypt.ComputeKey( passwordBytes, saltBytes, nCpuCost, rMemoryCost, pParallelization, null, output );

            Assert.That( output, Is.EqualTo( new byte[] 
            {
                0xfd, 0xba, 0xbe, 0x1c, 0x9d, 0x34, 0x72, 0x00, 0x78, 0x56, 0xe7, 0x19, 0x0d, 0x01, 0xe9, 0xfe,
                0x7c, 0x6a, 0xd7, 0xcb, 0xc8, 0x23, 0x78, 0x30, 0xe7, 0x73, 0x76, 0x63, 0x4b, 0x37, 0x31, 0x62,
                0x2e, 0xaf, 0x30, 0xd9, 0x2e, 0x22, 0xa3, 0x88, 0x6f, 0xf1, 0x09, 0x27, 0x9d, 0x98, 0x30, 0xda,
                0xc7, 0x27, 0xaf, 0xb9, 0x4a, 0x83, 0xee, 0x6d, 0x83, 0x60, 0xcb, 0xdf, 0xa2, 0xcc, 0x06, 0x40,
            } ) );
        }

        /// <summary>
        /// scrypt("pleaseletmein", "SodiumChloride", 16384, 8, 1, 64) = 70 23 bd cb 3a fd 73 48 46 1c 06 cd 81 fd 38 eb
        ///                                                              fd a8 fb ba 90 4f 8e 3e a9 b5 43 f6 54 5d a1 f2
        ///                                                              d5 43 29 55 61 3f 0f cf 62 d4 97 05 24 2a 9a f9
        ///                                                              e6 1e 85 dc 0d 65 1e 40 df cf 01 7b 45 57 58 87
        /// </summary>
        [Test]
        public void Scrypt_ComputeKey_TestVector3()
        {
            var password = "pleaseletmein";
            var passwordBytes = Encoding.ASCII.GetBytes( password );
            var salt = "SodiumChloride";
            var saltBytes = Encoding.ASCII.GetBytes( salt );
            var nCpuCost = 16384;
            var rMemoryCost = 8;
            var pParallelization = 1;
            const int derivedKeyLengthBytes = 64;
            var output = new byte[ derivedKeyLengthBytes ];

            SCrypt.ComputeKey( passwordBytes, saltBytes, nCpuCost, rMemoryCost, pParallelization, null, output );

            Assert.That( output, Is.EqualTo( new byte[] 
            {
                0x70, 0x23, 0xbd, 0xcb, 0x3a, 0xfd, 0x73, 0x48, 0x46, 0x1c, 0x06, 0xcd, 0x81, 0xfd, 0x38, 0xeb,
                0xfd, 0xa8, 0xfb, 0xba, 0x90, 0x4f, 0x8e, 0x3e, 0xa9, 0xb5, 0x43, 0xf6, 0x54, 0x5d, 0xa1, 0xf2,
                0xd5, 0x43, 0x29, 0x55, 0x61, 0x3f, 0x0f, 0xcf, 0x62, 0xd4, 0x97, 0x05, 0x24, 0x2a, 0x9a, 0xf9,
                0xe6, 0x1e, 0x85, 0xdc, 0x0d, 0x65, 0x1e, 0x40, 0xdf, 0xcf, 0x01, 0x7b, 0x45, 0x57, 0x58, 0x87,
            } ) );
        }

        /// <summary>
        /// scrypt(“pleaseletmein”, “SodiumChloride”, 1048576, 8, 1, 64) = 21 01 cb 9b 6a 51 1a ae ad db be 09 cf 70 f8 81
        ///                                                                ec 56 8d 57 4a 2f fd 4d ab e5 ee 98 20 ad aa 47
        ///                                                                8e 56 fd 8f 4b a5 d0 9f fa 1c 6d 92 7c 40 f4 c3
        ///                                                                37 30 40 49 e8 a9 52 fb cb f4 5c 6f a7 7a 41 a4
        /// </summary>
        [Test]
        public void Scrypt_ComputeKey_TestVector4()
        {
            var password = "pleaseletmein";
            var passwordBytes = Encoding.ASCII.GetBytes( password );
            var salt = "SodiumChloride";
            var saltBytes = Encoding.ASCII.GetBytes( salt );
            var nCpuCost = 1048576;
            var rMemoryCost = 8;
            var pParallelization = 1;
            const int derivedKeyLengthBytes = 64;
            var output = new byte[ derivedKeyLengthBytes ];

            SCrypt.ComputeKey( passwordBytes, saltBytes, nCpuCost, rMemoryCost, pParallelization, null, output );

            Assert.That( output, Is.EqualTo( new byte[] 
            {
                0x21, 0x01, 0xcb, 0x9b, 0x6a, 0x51, 0x1a, 0xae, 0xad, 0xdb, 0xbe, 0x09, 0xcf, 0x70, 0xf8, 0x81,
                0xec, 0x56, 0x8d, 0x57, 0x4a, 0x2f, 0xfd, 0x4d, 0xab, 0xe5, 0xee, 0x98, 0x20, 0xad, 0xaa, 0x47,
                0x8e, 0x56, 0xfd, 0x8f, 0x4b, 0xa5, 0xd0, 0x9f, 0xfa, 0x1c, 0x6d, 0x92, 0x7c, 0x40, 0xf4, 0xc3,
                0x37, 0x30, 0x40, 0x49, 0xe8, 0xa9, 0x52, 0xfb, 0xcb, 0xf4, 0x5c, 0x6f, 0xa7, 0x7a, 0x41, 0xa4,
            } ) );
        }
    }
}
